home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
EnigmA Amiga Run 1999 March
/
EnigmA AMIGA RUN 35 (1999)(G.R. Edizioni)(IT)[!][issue 1999-03].iso
/
earcd
/
devel
/
vbcc-68k-src
/
vlink
/
linker.c
< prev
next >
Wrap
C/C++ Source or Header
|
1999-01-01
|
46KB
|
1,476 lines
/* $VER: vlink linker.c V0.6a (19.12.98)
*
* This file is part of vlink, a portable linker for multiple
* object formats.
* Copyright (c) 1997-99 Frank Wille
*
* vlink is freeware and part of the portable and retargetable ANSI C
* compiler vbcc, copyright (c) 1995-99 by Volker Barthelmann.
* vlink may be freely redistributed as long as no modifications are
* made and nothing is charged for it. Non-commercial usage is allowed
* without any restrictions.
* EVERY PRODUCT OR PROGRAM DERIVED DIRECTLY FROM MY SOURCE MAY NOT BE
* SOLD COMMERCIALLY WITHOUT PERMISSION FROM THE AUTHOR.
*
*
* v0.6a (19.12.98) phx
* Endianess of linking process will be determined by the type
* of the first object.
* Support for little endian object file formats. All read/write
* operation on the object data is done in current endianess.
* v0.6 (24.10.98) phx
* Take base register section offset from FFFuncs.baseoff.
* v0.5e (05.10.98) phx
* Linking a ID_LIBARCH object requires its PriPointers to be
* added to the global list: add_priptrs().
* Call make_priptr_objects() after processing all objects in
* linker_resolve has finished. If there are entries in the Pri-
* Pointers list, then create some artificial sections (usually
* constructor and destructor lists).
* insertnode() <-> insertbehind().
* v0.5d (22.08.98) phx
* Faster memory allocation can be activated by #define FASTALLOC.
* Directories are only scanned if needed (AmigaOS filesystem is
* too slow).
* v0.5c (08.07.98) phx
* ID_ARTIFICIAL objects, i.e. objects created by the linker
* are supported.
* v0.5 (27.06.98) phx
* Target-specific linker symbol support.
* v0.4 (05.06.98) phx
* Rewrote linker_join(). Now it can be guaranteed, that first
* all code sections will be linked, then the data and finally
* the bss sections.
* Rewrote find_lnksect(). A lot more factors are now involved
* in the decision if two sections can be coalesced: relative
* references between the two, both are addressed base-relative,
* or target-specific factors.
* New functions for detection of relative references:
* addrelref(), copyrelrefs(), checkrelrefs().
* assign_common() finds a bss section for common symbols.
* v0.3b (02.05.98) phx
* There were still infinite loops, if an object comes without
* any section.
* v0.3 (16.04.98) phx
* Resolving ADDR16_HA/HI/LO references to a relocatable symbol
* didn't work (2 bytes of the next instructions were overwritten).
* Fixed print_function_name(). Didn't work, if symbols had
* a known size. Additionally, it can differentiate between
* 'no type', 'function' and 'object' now.
* Avoid infinite loops for object units without section in
* linker_join().
* v0.2 (07.03.98) phx
* Base relative relocations must not be resolved, if the output
* file is a relocatable object again. Base relative xrefs were
* even completely corrupted and overwrote the following
* instruction.
* Library units are linked immediately and are no longer always
* the last units in an output file.
* Base relative relocation didn't work for baseoff=0x8000.
* v0.1 (27.02.98) phx
* First version that seems to link AmigaOS ADOS and EHF
* objects and libraries. Many common features, like linking
* sections together which have relative references, are
* still missing. Also, PowerPC-ELF32 support is about to come.
* v0.0 (02.12.97) phx
* File created.
*/
#define LINKER_C
#include "vlink.h"
static char namebuf[FNAMEBUFSIZE];
static const char *filetypes[] = {
"unknown",
"object",
"executable",NULL,
"shared object",NULL,NULL,NULL,
"library"
};
static const char *sec_names[] = {
"undefined","code","data","bss",NULL
};
void linker_init(struct GlobalVars *);
void linker_load(struct GlobalVars *);
void linker_relrefs(struct GlobalVars *);
void linker_resolve(struct GlobalVars *);
void linker_join(struct GlobalVars *);
void linker_copy(struct GlobalVars *);
void linker_write(struct GlobalVars *);
void linker_cleanup(struct GlobalVars *);
char *getobjname(struct ObjectUnit *);
void print_function_name(struct Section *,unsigned long);
bool trace_sym_access(struct GlobalVars *,char *);
static void resolve_reloc(struct GlobalVars *,struct LinkedSection *,
struct Section *,struct Reloc *);
static void resolve_xref(struct GlobalVars *,struct LinkedSection *,
struct Section *,struct XReference *);
static void newreloc(struct LinkedSection *,struct LinkedSection *,
unsigned long,int32,uint8);
static void assign_common(struct GlobalVars *);
static char *maplibrary(struct GlobalVars *,struct InputFile *);
static char *searchlib(struct GlobalVars *,char *,int);
static char *scan_directory(char *,char *,int);
static void addrelref(struct RelRef **,struct Section *);
static void copyrelrefs(struct RelRef **,struct Section *);
static bool checkrelrefs(struct RelRef *,struct Section *);
static struct LinkedSection *create_lnksect(struct GlobalVars *,char *,
uint8,uint8,uint8,uint8);
static struct LinkedSection *find_lnksect(struct GlobalVars *,
struct Section *);
static void combine_sections(struct LinkedSection *,struct Section *,uint8);
static void print_symbol(FILE *,struct Symbol *);
static char *protstring(uint8);
void linker_init(struct GlobalVars *gv)
{
initlist(&gv->linkfiles);
initlist(&gv->selobjects);
initlist(&gv->libobjects);
initlist(&gv->sharedobjects);
gv->symbols = alloc_hashtable(SYMHTABSIZE);
initlist(&gv->pripointers);
gv->big_endian = -1;
}
void linker_load(struct GlobalVars *gv)
/* load all objects and libraries into memory, identify their */
/* format, then read all symbols and convert into internal format */
{
struct InputFile *ifn = (struct InputFile *)gv->inputlist.first;
struct InputFile *nextifn;
struct LinkFile *lf;
uint8 *objptr;
char *objname;
unsigned long objlen;
int i,ff;
if (gv->trace_file)
fprintf(gv->trace_file,"\nLoading files:\n\n");
while (nextifn = (struct InputFile *)ifn->n.next) {
if (ifn->lib) {
if (!(objptr = (uint8 *)maplibrary(gv,ifn))) {
sprintf(namebuf,"-l%s",ifn->name);
error(8,namebuf); /* cannot open -lxxx */
}
}
else {
if (objptr = (uint8 *)mapfile(ifn->name))
strcpy(namebuf,ifn->name);
else
error(8,ifn->name); /* cannot open xxx */
}
objlen = *(size_t *)(objptr - sizeof(size_t));
objname = base_name(namebuf);
/* determine the object's file format */
for (i=0,ff=ID_UNKNOWN; fff[i]; i++) {
if ((ff = (fff[i]->identify)(objname,objptr,objlen)) != ID_UNKNOWN)
break;
}
if (ff == ID_UNKNOWN)
error(11,objname); /* File format not recognized */
/* use endianess of first object read */
if (gv->big_endian < 0)
gv->big_endian = fff[i]->big_endian;
else if (gv->big_endian != fff[i]->big_endian)
error(61,objname); /* endianess differs from previous objects */
/* create new link file node */
lf = (struct LinkFile *)alloc(sizeof(struct LinkFile));
lf->pathname = allocstring(namebuf);
lf->filename = base_name(lf->pathname);
lf->data = objptr;
lf->length = objlen;
lf->format = (uint8)i;
lf->type = (uint8)ff;
if (gv->trace_file)
fprintf(gv->trace_file,"%s (%s %s)\n",namebuf,fff[i]->tname,
filetypes[ff]);
/* read the file and convert into internal format */
fff[i]->readconv(gv,lf);
addtail(&gv->linkfiles,&lf->n); /* not really needed */
ifn = nextifn; /* next input file */
}
}
void linker_resolve(struct GlobalVars *gv)
/* Resolve all symbol references and pull the required objects into */
/* the gv->selobjects list. */
{
bool priptrs_made = FALSE;
struct ObjectUnit *obj = (struct ObjectUnit *)gv->selobjects.first;
static const char *pulltxt = " needed due to ";
if (gv->trace_file)
fprintf(gv->trace_file,"\nDigesting symbol information:\n\n");
if (obj->n.next == NULL)
return; /* no objects in list */
do {
struct Section *sec = (struct Section *)obj->sections.first;
struct Section *nextsec;
struct XReference *xref,*nextxref;
struct Symbol *xdef;
struct ObjectUnit *pull_unit;
/* all sections of this object are checked for external references */
while (nextsec = (struct Section *)sec->n.next) {
xref = (struct XReference *)sec->xrefs.first;
while (nextxref = (struct XReference *)xref->n.next) {
if (!(xdef = findsymbol(gv,xref->name))) {
if (!(xdef = fff[gv->dest_format]->lnksymbol(gv,sec,xref))) {
/* undefined reference */
if (!gv->dest_object) {
print_function_name(sec,xref->offset);
error(21,getobjname(sec->obj),sec->name,xref->offset,
xref->name);
}
xref = nextxref;
continue;
}
}
/* reference resolved */
if (xdef->relsect) {
pull_unit = xdef->relsect->obj;
switch (pull_unit->lnkfile->type) {
case ID_ARTIFICIAL:
/* linker-generated object - insert it behind the curr. obj. */
if (gv->map_file) {
fprintf(gv->map_file,
"artificial object (%s) created due to %s\n",
pull_unit->objname,xref->name);
/* "artificial object (name.o) created due to @__name" */
}
insertbehind(&obj->n,&pull_unit->n);
pull_unit->flags |= OUF_LINKED;
/* turn into a normal object */
pull_unit->lnkfile->type = ID_OBJECT;
xref->symbol = xdef; /* unknown reference was resolved */
break;
case ID_SHAREDOBJ:
if (!(pull_unit->flags & OUF_LINKED)) {
if (gv->map_file) {
fprintf(gv->map_file,"%s%s%s\n",
pull_unit->lnkfile->pathname,pulltxt,xref->name);
/* Example: "/usr/lib/libc.so.12.0 needed due to _atexit" */
}
insertbehind(&obj->n,remnode(&pull_unit->n));
add_priptrs(gv,pull_unit);
pull_unit->flags |= OUF_LINKED;
}
break;
case ID_LIBARCH:
if (!(pull_unit->flags & OUF_LINKED)) {
if (gv->map_file) {
fprintf(gv->map_file,"%s (%s)%s%s\n",
pull_unit->lnkfile->pathname,pull_unit->objname,
pulltxt,xref->name);
/* "/usr/lib/libc.a (atexit.o) needed due to _atexit" */
}
insertbehind(&obj->n,remnode(&pull_unit->n));
add_priptrs(gv,pull_unit);
pull_unit->flags |= OUF_LINKED;
}
/* fall through */
default:
xref->symbol = xdef; /* unknown reference was resolved */
break;
}
}
else
xref->symbol = xdef;
xref = nextxref;
}
sec = nextsec; /* resolve ext. references of next section */
}
if (obj->n.next->next == NULL && !priptrs_made) {
make_priptr_objects(gv); /* PriPointer objects always at last */
priptrs_made = TRUE;
}
obj = (struct ObjectUnit *)obj->n.next;
}
while (obj->n.next);
}
void linker_relrefs(struct GlobalVars *gv)
/* All relocations and unresolved xrefs with a relative reference */
/* to other sections are collected. A second task is to detect */
/* base-relative references and set the approriate flag of the */
/* referenced section. */
{
struct ObjectUnit *obj = (struct ObjectUnit *)gv->selobjects.first;
struct ObjectUnit *nextobj;
/*@@@ find a better place for this: */
assign_common(gv); /* find a bss section for common symbols */
while (nextobj = (struct ObjectUnit *)obj->n.next) {
struct Section *sec = (struct Section *)obj->sections.first;
struct Section *nextsec;
while (nextsec = (struct Section *)sec->n.next) {
struct Symbol *xdef;
struct XReference *xref = (struct XReference *)sec->xrefs.first;
struct XReference *nextxref;
struct Reloc *reloc = (struct Reloc *)sec->relocs.first;
struct Reloc *nextreloc;
struct RelRef **rr = &sec->relrefs;
*rr = NULL;
while (nextxref = (struct XReference *)xref->n.next) {
if (xdef = xref->symbol) {
if (xdef->relsect!=NULL && xdef->relsect!=sec &&
(xdef->type==SYM_RELOC || xdef->type==SYM_COMMON)) {
if ((xref->type & R_MASK) == R_REL)
/* relative reference to different section */
addrelref(rr,xdef->relsect);
else if ((xref->type & R_MASK) == R_BASEREL)
/* other section is accessed base relative from this one */
xdef->relsect->flags |= SF_SMALLDATA;
}
}
xref = nextxref;
}
while (nextreloc = (struct Reloc *)reloc->n.next) {
if (reloc->relocsect.ptr != sec) {
if ((reloc->type & R_MASK) == R_REL)
/* relative reference to different section */
addrelref(rr,reloc->relocsect.ptr);
else if ((reloc->type & R_MASK) == R_BASEREL)
/* other section is accessed base relative from this one */
reloc->relocsect.ptr->flags |= SF_SMALLDATA;
}
reloc = nextreloc;
}
sec = nextsec;
}
obj = nextobj;
}
}
void linker_join(struct GlobalVars *gv)
/* Join the sections with same name and type. Calculate their */
/* virtual address and size. */
{
struct ObjectUnit *obj,*nextobj;
uint8 stype;
if (gv->trace_file)
fprintf(gv->trace_file,"Joining selected sections:\n\n");
for (stype=0; stype<=ST_LAST; stype++) {
/* join sections, beginning with ST_CODE, then ST_DATA and ST_UDATA */
obj = (struct ObjectUnit *)gv->selobjects.first;
while (nextobj = (struct ObjectUnit *)obj->n.next) {
struct Section *sec = (struct Section *)obj->sections.first;
struct Section *nextsec;
if (obj->lnkfile->type != ID_SHAREDOBJ) {
while (nextsec = (struct Section *)sec->n.next) {
struct LinkedSection *ls;
if (sec->type == stype) {
unsigned long abytes;
if (!(ls = find_lnksect(gv,sec))) {
ls = create_lnksect(gv,sec->name,sec->type,sec->flags,
sec->protection,sec->alignment);
}
abytes = align(ls->base+ls->size,sec->alignment);
sec->lnksec = ls;
sec->offset = ls->size + abytes;
sec->va = ls->base + sec->offset;
ls->size += sec->size + abytes;
if (!(sec->flags & SF_UNINITIALIZED))
ls->filesize += sec->size + abytes;
/* move into LinkedSection */
copyrelrefs(&ls->relrefs,sec);
addtail(&ls->sections,remnode(&sec->n));
}
sec = nextsec;
}
}
obj = nextobj;
}
}
if (gv->map_file) {
/* print file names and the new addresses of their sections */
fprintf(gv->map_file,"\nFiles:\n\n");
obj = (struct ObjectUnit *)gv->selobjects.first;
while (nextobj = (struct ObjectUnit *)obj->n.next) {
struct LinkFile *lfile = obj->lnkfile;
struct LinkedSection *ls = (struct LinkedSection *)gv->lnksec.first;
struct LinkedSection *nextls;
char sep = ' ';
if (lfile->type == ID_LIBARCH)
fprintf(gv->map_file," %s (%s)",lfile->pathname,obj->objname);
else
fprintf(gv->map_file," %s",obj->objname);
if (lfile->type != ID_SHAREDOBJ) {
while (nextls = (struct LinkedSection *)ls->n.next) {
struct Section *sec = (struct Section *)ls->sections.first;
struct Section *nextsec;
while (nextsec = (struct Section *)sec->n.next) {
if (sec->obj == obj) { /* section came from this object? */
fprintf(gv->map_file,"%c %s %lx(%lx)",sep,ls->name,
sec->va,sec->size);
sep = ',';
}
sec = nextsec;
}
ls = nextls;
}
}
if (sep == ',') /* any sections listed? */
fprintf(gv->map_file," hex\n");
else
fprintf(gv->map_file," symbols only\n"); /* empty or shared obj. */
obj = nextobj;
}
}
}
void linker_copy(struct GlobalVars *gv)
/* Merge contents of linked sections, fix symbol offsets and */
/* allocate common symbol data. */
{
struct LinkedSection *ls = (struct LinkedSection *)gv->lnksec.first;
struct LinkedSection *nextls;
struct Section *sec,*nextsec;
if (gv->trace_file)
fprintf(gv->trace_file,"\n");
if (gv->map_file)
fprintf(gv->map_file,"\n");
while (nextls = (struct LinkedSection *)ls->n.next) {
struct Section *sec = (struct Section *)ls->sections.first;
struct Section *nextsec;
if (gv->trace_file)
fprintf(gv->trace_file,"Copying %s:\n\n",ls->name);
if (gv->map_file)
fprintf(gv->map_file,"\nSymbols of %s:\n\n",ls->name);
if (!(ls->flags&SF_UNINITIALIZED))
ls->data = alloczero(ls->size);
while (nextsec = (struct Section *)sec->n.next) {
struct Symbol *sym;
int i;
unsigned long n;
if (ls->data && sec->data) {
/* copy section contents */
memcpy(ls->data+sec->offset,sec->data,sec->size);
}
/* find section symbols and fix their offsets */
for (i=0; i<OBJSYMHTABSIZE; i++) {
sym = sec->obj->objsyms[i];
while (sym) {
if (sym->relsect == sec) { /* symbol defined in this section */
switch (sym->type) {
case SYM_RELOC:
sym->value += sec->offset; /* fix offset for reloc symbols */
break;
case SYM_COMMON:
if (sec->obj->lnkfile->type != ID_SHAREDOBJ) {
/* allocate common symbol and transform into SYM_RELOC */
if (!(sec->type&SF_UNINITIALIZED))
ierror("linker_relocate(): Common %s was defined in "
"%s of %s, which is not a bss-type section",
sym->name,sec->name,sec->obj->objname);
n = align(ls->base+ls->size,sym->value);
sym->value = ls->size + n;
sym->type = SYM_RELOC;
ls->size += n + sym->size;
if (gv->map_file)
fprintf(gv->map_file,
"Allocating common %s: %x at %x\n",
sym->name,(int)sym->size,
(int)(ls->base+sym->value));
}
break;
}
addtail(&ls->symbols,&sym->n);
if (gv->map_file)
print_symbol(gv->map_file,sym);
}
sym = sym->obj_chain;
}
}
sec = nextsec;
}
ls = nextls;
}
}
void linker_relocate(struct GlobalVars *gv)
/* Fix relocations, resolve x-references and create more relocations, */
/* if required. */
{
struct LinkedSection *ls = (struct LinkedSection *)gv->lnksec.first;
struct LinkedSection *nextls;
struct Section *sec,*nextsec;
while (nextls = (struct LinkedSection *)ls->n.next) {
struct Section *sec = (struct Section *)ls->sections.first;
struct Section *nextsec;
if (!(ls->flags&SF_UNINITIALIZED)) {
if (gv->trace_file)
fprintf(gv->trace_file,"Relocating %s:\n\n",ls->name);
while (nextsec = (struct Section *)sec->n.next) {
struct Reloc *rel;
struct XReference *xref;
/* copy and fix relocations */
while (rel = (struct Reloc *)remhead(&sec->relocs)) {
rel->offset += sec->offset;
rel->addend += rel->relocsect.ptr->offset;
rel->relocsect.lnk = rel->relocsect.ptr->lnksec;
if ((rel->type&R_MASK) != R_ADDR) {
/* relative relocations can be resolved */
resolve_reloc(gv,ls,sec,rel);
}
else {
/* absolute relocations remain */
writesection(gv,ls->data+rel->offset,rel->type,
(uint32)rel->addend); /* write new addend */
addtail(&ls->relocs,&rel->n);
}
}
/* resolve, fix and copy x-references */
while (xref = (struct XReference *)remhead(&sec->xrefs)) {
xref->offset += sec->offset;
if (xref->symbol)
/* resolve xref - turn into relocation, if required */
resolve_xref(gv,ls,sec,xref);
else
/* xref remains in output file */
addtail(&ls->xrefs,&xref->n);
}
sec = nextsec;
}
}
ls = nextls;
}
}
void linker_write(struct GlobalVars *gv)
{
FILE *f;
if (!gv->errflag) { /* no error? */
if (gv->trace_file)
fprintf(gv->trace_file,"\nCreating output file %s (%s).\n",
gv->dest_name,fff[gv->dest_format]->tname);
/* write output file */
if (f = fopen(gv->dest_name,"w")) {
if (gv->dest_sharedobj)
fff[gv->dest_format]->writeshared(gv,f);
else if (gv->dest_object)
fff[gv->dest_format]->writeobject(gv,f);
else
fff[gv->dest_format]->writeexec(gv,f);
fclose(f);
}
else /* Can't create output file */
error(29,gv->dest_name);
}
}
void linker_cleanup(struct GlobalVars *gv)
{
}
static void resolve_reloc(struct GlobalVars *gv,struct LinkedSection *ls,
struct Section *sec,struct Reloc *r)
/* Resolve relative and base-relative relocations. */
{
char be = gv->big_endian;
uint8 *p = ls->data + r->offset;
int biterr = 0;
if ((r->type&R_MASK) == R_REL) {
/*********************************/
/* Normal, PC-relative reference */
/*********************************/
if (r->relocsect.lnk == ls) {
/* Relative reloc offset must be from the same section */
int32 d = r->addend - r->offset;
switch (r->type) {
case R_REL32:
write32(be,p,(uint32)d);
break;
case R_REL26:
biterr = checkrange(d,3,TRUE);
write32(be,p,(read32(be,p) & 0xfc000003)
| (((uint32)d)&0x3fffffc));
break;
case R_REL16:
biterr = checkrange(d,2,TRUE);
write16(be,p,(uint16)d);
break;
/* @@@ currently no differentiation between the following */
case R_REL14:
case R_REL14_BRTAKEN:
case R_REL14_BRNTAKEN:
biterr = checkrange(d,2,TRUE);
write16(be,p,(read16(be,p) & 3) | (((uint16)d)&0xfffc));
break;
case R_REL8:
biterr = checkrange(d,1,TRUE);
*p = (uint8)d;
break;
default: /* relative relocation type not supported */
ierror("resolve_reloc(): Unsupported relative relocation "
"type %d",(int)r->type);
break;
}
}
else { /* error: Illegal relative reference on a different section */
print_function_name(sec,r->offset);
error(24,getobjname(sec->obj),sec->name,r->offset-sec->offset,
r->relocsect.lnk->name,r->addend);
}
}
/***************************/
/* Base relative reference */
/***************************/
else {
if (!gv->dest_object) {
/* resolve base-relative relocation for executable file */
int32 d = r->addend - fff[gv->dest_format]->baseoff;
switch (r->type) {
case R_BASEREL32:
write32(be,p,(uint32)d);
break;
case R_BASEREL26:
biterr = checkrange(d,3,TRUE);
write32(be,p,(read32(be,p) & 0xfc000003)
| (((uint32)d)&0x3fffffc));
break;
case R_BASEREL16:
biterr = checkrange(d,2,TRUE);
write16(be,p,(uint16)d);
break;
case R_BASEREL8:
biterr = checkrange(d,1,TRUE);
*p = (uint8)d;
break;
default: /* base-relative relocation type not supported */
ierror("resolve_reloc(): Unsupported base relative relocation "
"type %d",(int)r->type);
break;
}
}
else {
/* destination is a relocatable object file, */
/* so create a new base-relative relocation entry */
int32 d = r->addend;
switch (r->type) {
case R_BASEREL32:
write32(be,p,(uint32)d);
break;
case R_BASEREL26:
biterr = checkrange(d,3,TRUE);
write32(be,p,(read32(be,p) & 0xfc000003)
| (((uint32)d) & 0x3fffffc));
break;
case R_BASEREL16:
biterr = checkrange(d,2,TRUE);
write16(be,p,(uint16)d);
break;
case R_BASEREL8:
biterr = checkrange(d,1,TRUE);
*p = (uint8)d;
break;
default: /* base-relative relocation type not supported */
ierror("resolve_reloc(): Unsupported base relative relocation "
"type %d",(int)r->type);
break;
}
newreloc(ls,r->relocsect.lnk,r->offset,r->addend,r->type);
}
if (biterr) { /* relative reference out of range! */
print_function_name(sec,r->offset);
error(25,getobjname(sec->obj),sec->name,r->offset-sec->offset,
biterr,r->relocsect.lnk->name,r->addend);
}
}
}
static void resolve_xref(struct GlobalVars *gv,struct LinkedSection *ls,
struct Section *sec,struct XReference *xref)
/* The external reference xref can be resolved by the symbol definition */
/* xdef = xref->symbol. The assignment of a relocatable symbol requires */
/* the creation of a new Reloc entry for the section. */
{
static char *ierr_func = "resolve_xref(): ";
static char *ierr_rtype = "Unsupported xref relocation type ";
struct Symbol *xdef = xref->symbol;
char be = gv->big_endian;
uint8 *p = ls->data + xref->offset;
int32 x;
int biterr = 0;
/* last chance to initialize a target-specific linker symbol */
if (xdef->flags & SYMF_LNKSYM)
fff[gv->dest_format]->setlnksym(gv,xdef,xref);
/**************************************************/
/* Relative/absolute reference to absolute symbol */
/**************************************************/
if (xdef->type == SYM_ABS) {
/* resolve x-reference to an absolute symbol */
x = (int32)xdef->value + xref->addend;
switch (xref->type) {
case R_ADDR32:
case R_REL32:
case R_BASEREL32:
write32(be,p,(uint32)x);
break;
case R_ADDR26:
biterr = checkrange(x,3,FALSE);
write32(be,p,(read32(be,p) & 0xfc000003)
| (((uint32)x)&0x3fffffc));
break;
case R_REL26:
case R_BASEREL26:
biterr = checkrange(x,3,TRUE);
write32(be,p,(read32(be,p) & 0xfc000003)
| (((uint32)x)&0x3fffffc));
break;
case R_ADDR16:
biterr = checkrange(x,2,FALSE);
write16(be,p,(uint16)x);
break;
/* @@@ currently no differentiation between the following */
case R_ADDR14:
case R_ADDR14_BRTAKEN:
case R_ADDR14_BRNTAKEN:
biterr = checkrange(x,2,FALSE);
write16(be,p,(read16(be,p) & 3) | (((uint16)x)&0xfffc));
break;
case R_REL16:
case R_BASEREL16:
biterr = checkrange(x,2,TRUE);
write16(be,p,(uint16)x);
break;
/* @@@ currently no differentiation between the following */
case R_REL14:
case R_REL14_BRTAKEN:
case R_REL14_BRNTAKEN:
biterr = checkrange(x,2,TRUE);
write16(be,p,(read16(be,p) & 3) | (((uint32)x)&0xfffc));
break;
case R_ADDR16_LO:
write16(be,p,(uint16)((uint32)x&0xffff));
break;
case R_ADDR16_HI:
write16(be,p,(uint16)(((uint32)x>>16)&0xffff));
break;
case R_ADDR16_HA:
write16(be,p,(uint16)((((uint32)x+0x8000)>>16)&0xffff));
break;
case R_ADDR8:
biterr = checkrange(x,1,FALSE);
*p = (uint8)x;
break;
case R_REL8:
case R_BASEREL8:
biterr = checkrange(x,1,TRUE);
*p = (uint8)x;
break;
default: /* xref relocation type not supported */
ierror("%s%s%d",ierr_func,ierr_rtype,(int)xref->type);
break;
}
if (biterr) { /* referenced absolute symbol out of range! */
print_function_name(sec,xref->offset);
error(26,getobjname(sec->obj),sec->name,xref->offset-sec->offset,
xdef->name,xdef->value,(uint32)xref->addend,biterr);
}
}
else if (xdef->type == SYM_RELOC) {
/* resolve x-reference to a relocatable symbol */
/********************************************/
/* Relative reference to relocatable symbol */
/********************************************/
if ((xref->type & R_MASK) == R_REL) {
/* relative xrefs must be resolved within the same section */
if (xdef->relsect->lnksec != ls) {
/* Illegal relative reference to symbol */
print_function_name(sec,xref->offset);
error(27,getobjname(sec->obj),sec->name,xref->offset-sec->offset,
xdef->name);
return;
}
x = (int32)xdef->value - (int32)xref->offset + xref->addend;
switch (xref->type) {
case R_REL32:
write32(be,p,(uint32)x);
break;
case R_REL26:
biterr = checkrange(x,3,TRUE);
write32(be,p,(read32(be,p) & 0xfc000003)
| (((uint32)x)&0x3fffffc));
break;
case R_REL16:
biterr = checkrange(x,2,TRUE);
write16(be,p,(uint16)x);
break;
/* @@@ currently no differentiation between the following */
case R_REL14:
case R_REL14_BRTAKEN:
case R_REL14_BRNTAKEN:
biterr = checkrange(x,2,TRUE);
write16(be,p,(read16(be,p) & 3) | (((uint16)x)&0xfffc));
break;
case R_REL8:
biterr = checkrange(x,1,TRUE);
*p = (uint8)x;
break;
default: /* xref relocation type not supported */
ierror("%s%s%d",ierr_func,ierr_rtype,(int)xref->type);
break;
}
if (biterr) { /* Relative reference to reloc symbol out of range! */
print_function_name(sec,xref->offset);
error(28,getobjname(sec->obj),sec->name,xref->offset-sec->offset,
xdef->name,xdef->value,(uint32)xref->addend,biterr);
}
}
/*************************************************/
/* Base relative reference to relocatable symbol */
/*************************************************/
else if ((xref->type&R_MASK)==R_BASEREL) {
if (!gv->dest_object) { /* destination is executable file? */
int32 d = ((int32)xdef->value + xref->addend) -
fff[gv->dest_format]->baseoff;
switch (xref->type) {
case R_BASEREL32:
write32(be,p,(uint32)d);
break;
case R_BASEREL26:
biterr = checkrange(d,3,TRUE);
write32(be,p,(read32(be,p) & 0xfc000003)
| (((uint32)d)&0x3fffffc));
break;
case R_BASEREL16:
biterr = checkrange(d,2,TRUE);
write16(be,p,(uint16)d);
break;
case R_BASEREL8:
biterr = checkrange(d,1,TRUE);
*p = (uint8)d;
break;
default: /* xref relocation type not supported */
ierror("%s%s%d",ierr_func,ierr_rtype,(int)xref->type);
break;
}
}
else {
/* destination is a relocatable object file, */
/* so create a new base-relative relocation entry */
int32 d = (int32)xdef->value + xref->addend;
switch (xref->type) {
case R_BASEREL32:
write32(be,p,(uint32)d);
break;
case R_BASEREL26:
biterr = checkrange(d,3,TRUE);
write32(be,p,(read32(be,p) & 0xfc000003)
| (((uint32)d) & 0x3fffffc));
break;
case R_BASEREL16:
biterr = checkrange(d,2,TRUE);
write16(be,p,(uint16)d);
break;
case R_BASEREL8:
biterr = checkrange(d,1,TRUE);
*p = (uint8)d;
break;
default: /* xref relocation type not supported */
ierror("%s%s%d",ierr_func,ierr_rtype,(int)xref->type);
break;
}
newreloc(ls,xdef->relsect->lnksec,xref->offset,d,xref->type);
}
if (biterr) { /* Base relative ref. to reloc symbol out of range! */
print_function_name(sec,xref->offset);
error(36,getobjname(sec->obj),sec->name,xref->offset-sec->offset,
xdef->name,xdef->value,(uint32)xref->addend,biterr);
}
}
/********************************************/
/* Absolute reference to relocatable symbol */
/********************************************/
else {
/* generates a new relocation entry */
int32 x = (int32)xdef->value + xref->addend;
switch (xref->type) {
case R_ADDR16_LO:
write16(be,p,(uint16)((uint32)x&0xffff));
break;
case R_ADDR16_HI:
write16(be,p,(uint16)(((uint32)x>>16)&0xffff));
break;
case R_ADDR16_HA:
write16(be,p,(uint16)((((uint32)x+0x8000)>>16)&0xffff));
break;
default:
write32(be,p,(uint32)x);
break;
}
newreloc(ls,xdef->relsect->lnksec,xref->offset,x,xref->type);
}
}
else
ierror("%sIllegal xdef-symbol type: %d",ierr_func,(int)xdef->type);
}
static void newreloc(struct LinkedSection *current_ls,
struct LinkedSection *base_ls,
unsigned long offs,int32 addend,uint8 type)
/* Create a new relocation node and insert it into the relocation */
/* list of the current LinkedSection. */
{
struct Reloc *r = alloc(sizeof(struct Reloc));
r->relocsect.lnk = base_ls;
r->offset = offs;
r->addend = addend;
r->type = type;
addtail(¤t_ls->relocs,&r->n);
}
static void assign_common(struct GlobalVars *gv)
/* Assign common symbols to a BSS section */
{
struct ObjectUnit *obj = (struct ObjectUnit *)gv->selobjects.first;
struct ObjectUnit *nextobj;
struct Symbol *sym;
int i;
while (nextobj = (struct ObjectUnit *)obj->n.next) {
if (obj->lnkfile->type != ID_SHAREDOBJ) {
for (i=0; i<OBJSYMHTABSIZE; i++) {
sym = obj->objsyms[i];
while (sym) {
if (sym->type == SYM_COMMON) {
if (!(sym->relsect = find_sect_type(obj,ST_UDATA,
SP_READ|SP_WRITE))) {
/* No bss section? Then create a new one. */
/* @@@ maybe it would be better to use the object's */
/* file format rather than the dest. file format? */
sym->relsect = fff[gv->dest_format]->bssdefault(obj);
}
}
sym = sym->obj_chain;
}
}
}
obj = nextobj;
}
}
char *getobjname(struct ObjectUnit *obj)
/* if library: return "file name(object name)" */
/* else: return "file name" */
{
char *fn = obj->lnkfile->filename;
if (obj->lnkfile->type == ID_LIBARCH) {
char *on = obj->objname;
if (strlen(fn)+strlen(on)+2 < FNAMEBUFSIZE) {
sprintf(namebuf,"%s(%s)",fn,on);
return (namebuf);
}
}
return (fn);
}
void print_function_name(struct Section *sec,unsigned long offs)
/* Try to determine the function to which the section offset */
/* belongs, by comparing with SYMI_FUNC-type symbol definitions. */
/* If this was successful and the current function is different */
/* from the last one printed, make an output to stderr. */
{
static const char *infoname[] = { "", "object ", "function " };
static struct Symbol *last_func=NULL;
struct Symbol *sym,*func=NULL;
int i;
for (i=0; i<OBJSYMHTABSIZE; i++) {
sym = sec->obj->objsyms[i]; /* scan all hash chains */
while (sym) {
if (sym->relsect == sec) {
if (sym->info <= SYMI_FUNC) {
if (sym->type == SYM_RELOC) {
if ((unsigned long)sym->value <= offs) {
if (sym->size) { /* size of function specified? */
if ((unsigned long)(sym->value+sym->size) > offs) {
func = sym;
i = OBJSYMHTABSIZE;
break; /* function found! */
}
}
else { /* no size - find nearest... */
if (func) {
if (sym->value > func->value)
func = sym;
}
else
func = sym;
}
}
}
}
}
sym = sym->obj_chain;
}
}
/* print function name */
if (func && func!=last_func) {
last_func = func;
fprintf(stderr,"%s: In %s\"%s\":\n",getobjname(sec->obj),
infoname[func->info],func->name);
}
}
static char *maplibrary(struct GlobalVars *gv,struct InputFile *ifn)
/* Map a complete file into memory and return its address. */
/* The file's length is returned in *(p-sizeof(size_t)). */
/* All defined library paths will be searched for the file, */
/* before aborting. On success, the complete path of the loaded */
/* file is stored in namebuf[]. */
{
char *p;
char libname[FNAMEBUFSIZE];
if (strlen(ifn->name) < (FNAMEBUFSIZE-16)) {
if (ifn->dynamic) {
sprintf(libname,"lib%s.so",ifn->name);
if (p = searchlib(gv,libname,ifn->so_ver))
return (p);
}
sprintf(libname,"lib%s.a",ifn->name);
if (p = searchlib(gv,libname,-1))
return (p);
sprintf(libname,"%s.lib",ifn->name);
if (p = searchlib(gv,libname,-1))
return (p);
}
return (NULL);
}
static char *searchlib(struct GlobalVars *gv,char *libname,int so_ver)
{
struct LibPath *lpn = (struct LibPath *)gv->libpaths.first;
struct LibPath *nextlpn;
char *p;
if (p = scan_directory(".",libname,so_ver))
return (p);
while (nextlpn = (struct LibPath *)lpn->n.next) {
if (p = scan_directory(lpn->path,libname,so_ver))
return (p);
lpn = nextlpn;
}
return (NULL);
}
static char *scan_directory(char *dirname,char *libname,int so_ver)
{
size_t lnlen=strlen(libname);
char *dd,*fname=NULL;
if (so_ver < 0) {
/* no need to scan the directory */
fname = libname;
}
else {
if (dd = open_dir(dirname)) {
while (fname = read_dir(dd)) {
if (!strncmp(fname,libname,lnlen)) {
/* found a library archive/shared object name! */
if (so_ver > 0) { /* check version of shared object */
if (fname[lnlen]=='.' && fname[lnlen+1]!=0) {
if (atoi(&fname[lnlen+1]) >= so_ver)
break;
}
}
else {
/* otherwise the version is ignored - @@@ take highest ver. ? */
break;
}
}
else
fname = NULL;
}
close_dir(dd);
}
}
if (fname) {
char *p;
size_t dnlen;
if ((dnlen = strlen(dirname)) < (FNAMEBUFSIZE-strlen(fname)-2)) {
strcpy(namebuf,dirname);
if (namebuf[dnlen-1]!='/' && namebuf[dnlen-1]!=':') {
namebuf[dnlen++] = '/';
namebuf[dnlen] = 0;
}
strncat(namebuf,fname,FNAMEBUFSIZE-dnlen-1);
if (p = mapfile(namebuf))
return (p);
}
}
return (NULL);
}
static void addrelref(struct RelRef **rrptr,struct Section *sec)
/* if not already exists, add a new relative reference to another sect. */
{
struct RelRef *rr;
if (rr = *rrptr) {
while (rr->next) {
if (rr->refsec == sec)
return; /* reference already exists */
rr = rr->next;
}
}
else
rr = (struct RelRef *)rrptr;
rr->next = alloczero(sizeof(struct RelRef));
rr->next->refsec = sec;
}
static void copyrelrefs(struct RelRef **rrptr,struct Section *sec)
/* transfer all rel. references from a single section to a linked-section */
{
struct RelRef *old,*srr = sec->relrefs;
while (old = srr) {
addrelref(rrptr,srr->refsec);
srr = srr->next;
#ifndef FASTALLOC
free(old);
#endif
}
sec->relrefs = NULL;
}
static bool checkrelrefs(struct RelRef *rr,struct Section *sec)
/* Check, if there is a relative reference to the specified section. */
{
while (rr) {
if (rr->refsec == sec)
return (TRUE);
rr = rr->next;
}
return (FALSE);
}
bool trace_sym_access(struct GlobalVars *gv,char *name)
/* check if the symbol with this name should be traced */
{
struct SymNames *sn;
if (gv->trace_syms) {
sn = gv->trace_syms[elf_hash(name)%TRSYMHTABSIZE];
while (sn) {
if (!strcmp(name,sn->name))
return (TRUE); /* symbol found! */
sn = sn->next;
}
}
return (FALSE);
}
static struct LinkedSection *create_lnksect(struct GlobalVars *gv,char *name,
uint8 type,uint8 flags,
uint8 protection,uint8 alignment)
/* create and initialize a LinkedSection node and include */
/* it in the global list */
{
struct LinkedSection *ls = alloc(sizeof(struct LinkedSection));
ls->index = gv->nsecs++;
ls->name = name;
ls->type = type;
ls->flags = flags;
ls->protection = protection;
ls->alignment = alignment;
ls->base = fff[gv->dest_format]->secbase(gv,ls);
ls->size = ls->filesize = 0;
initlist(&ls->sections);
ls->relrefs = NULL;
ls->data = NULL;
initlist(&ls->relocs);
initlist(&ls->xrefs);
initlist(&ls->symbols);
addtail(&gv->lnksec,&ls->n);
return (ls);
}
static struct LinkedSection *find_lnksect(struct GlobalVars *gv,
struct Section *sec)
/* find a LinkedSection node which fits the specified section */
{
struct LinkedSection *lsn = (struct LinkedSection *)gv->lnksec.first;
struct LinkedSection *nextlsn;
uint8 f;
int tl;
while (nextlsn = (struct LinkedSection *)lsn->n.next) {
if ((f = fff[gv->dest_format]->cmpsecflags(lsn->flags,sec->flags))
!= 0xff) {
f &= ~SF_PORTABLE_MASK;
/* target-specific linking */
if ((tl = fff[gv->dest_format]->targetlink(gv,lsn,sec)) > 0) {
/* target requires combining of sections */
combine_sections(lsn,sec,f);
return (lsn);
}
if (tl == 0) { /* target wants to use the default rules */
if (!gv->dest_object) {
/* only, if an executable file will be created: */
if (gv->small_code && lsn->type==sec->type==ST_CODE) {
/* merge all code sections */
combine_sections(lsn,sec,f);
return (lsn);
}
if (gv->small_data) {
if ((lsn->type==ST_DATA || lsn->type==ST_UDATA) &&
(sec->type==ST_DATA || sec->type==ST_UDATA)) {
/* merge all data and bss sections */
combine_sections(lsn,sec,f);
return (lsn);
}
}
if (checkrelrefs(lsn->relrefs,sec)) {
/* we must link them together, because there are rel. refs */
if ((lsn->type==ST_CODE || sec->type==ST_CODE) &&
lsn->type != sec->type)
/* induces a maybe unwanted combination of code and data */
error(58,sec_names[lsn->type],lsn->name,sec_names[sec->type],
sec->name,getobjname(sec->obj));
combine_sections(lsn,sec,f);
return (lsn);
}
if (!gv->multibase && (lsn->flags & sec->flags & SF_SMALLDATA)) {
combine_sections(lsn,sec,f);
return (lsn);
}
}
/* standard check, if sections could be coalesced */
if (!strcmp(sec->name,lsn->name) || /* same name or no name */
*(sec->name)==0) {
if (lsn->type == sec->type) { /* same type */
combine_sections(lsn,sec,f);
return (lsn);
}
}
} /* default rules */
}
lsn = nextlsn;
}
return (NULL);
}
static void combine_sections(struct LinkedSection *lsn,struct Section *sec,
uint8 target_flags)
{
if (lsn->type == ST_UNDEFINED) {
lsn->type = sec->type;
lsn->flags = sec->flags;
}
else {
lsn->flags &= SF_PORTABLE_MASK;
lsn->flags |= (sec->flags&SF_SMALLDATA) | target_flags;
}
if (lsn->protection != sec->protection) {
if (lsn->protection &&
(lsn->protection & sec->protection) != sec->protection) {
/* combine protection-flags of the two sections */
char prot1[5],prot2[5];
strncpy(prot1,protstring(lsn->protection),5);
strncpy(prot2,protstring(lsn->protection|sec->protection),5);
error(22,lsn->name,prot1,prot2);
}
lsn->protection |= sec->protection;
}
if (sec->alignment > lsn->alignment) {
/* increase alignment to fit the needs of the new section */
if (lsn->alignment)
error(23,lsn->name,lsn->alignment,sec->alignment);
lsn->alignment = sec->alignment;
}
}
static void print_symbol(FILE *f,struct Symbol *sym)
/* print symbol name, type, value, etc. */
{
fprintf(f," %s: %s%s%s, value 0x%x, size %d\n",sym->name,
sym_bind[sym->bind],sym_type[sym->type],sym_info[sym->info],
(int)sym->value,(int)sym->size);
}
static char *protstring(uint8 prot)
/* return pointer to protection string - example: "r---" or "rwxs" */
{
static char *ps="----";
char *p = ps;
*p++ = (prot & SP_READ) ? 'r' : '-';
*p++ = (prot & SP_WRITE) ? 'w' : '-';
*p++ = (prot & SP_EXEC) ? 'x' : '-';
*p++ = (prot & SP_SHARE) ? 's' : '-';
return (ps);
}